#ifndef _M_BUFFER_H_
#define _M_BUFFER_H_

// 单个缓冲区的进一步设计:
// 设计一个缓冲区：直接存放格式化后的日志消息字符串
// 1/好处:
// 减少了 LogMsg 对象频繁的构造的消耗
// 可以针对缓冲区中的日志消息，一次性进行 IO 操作，减少 IO 次数，提高效率
// 2/缓冲区类的设计:
// 管理一个存放字符串数据的缓冲区（使用 vector 进行空间管理）
// 当前的写入数据位置的指针（指向可写区域的起始位置，避免数据的写入覆盖）
// 当前的读取数据位置的指针（指向可读数据区域的起始位置，当读取指针与写入指针指向相同位置表示数据取完了）
// 3/提供的操作:
// 向缓冲区中写入数据
// 获取可读数据起始地址的接口
// 获取可读数据长度的接口
// 移动读写位置的接口
// 初始化缓冲区的操作（将读写位置初始化 -- 将一个缓冲区所有数据处理完毕之后）
// 提供交换缓冲区的操作（交换空间地址，并不交换空间数据）
// 4/向缓冲区写入：
// 扩容    阻塞 / 返回
// 我们可以两种都提供！第一种一般只能用来测试极限性能（就是不考虑写入失败的情况！）
//但是在实际开发中并不考虑使用扩容，因为我们的空间并不是无限制使用的，因为这样很危险，
//在极端情况下比如某个程序陷入了无限死循环，那么导致缓冲区爆满系统崩溃
#include "util.hpp"
#include <vector>
#include <cassert>

namespace logsys {
// 默认缓冲区大小为 1MB
#define BUFFER_DEFAULT_SIZE (1*1024*1024)
// 缓冲区每次扩容增加的大小为 1MB
#define BUFFER_INCREMENT_SIZE (1*1024*1024)
// 缓冲区大小阈值为 10MB
#define BUFFER_THRESHOLD_SIZE (10*1024*1024)

class Buffer {
public:
    // 构造函数，初始化缓冲区大小为默认值，读写索引为0
    Buffer() : _buffer(BUFFER_DEFAULT_SIZE), _writer_idx(0), _reader_idx(0) {}

    // 向缓冲区写入数据
    void push(const char *data, size_t len) {
        // 缓冲区剩余空间不够的情况：1. 扩容。
        // 1. 固定大小， 则直接返回
       // if (len > writeAbleSize()) return;
        ensureEnoughSize(len);//buffer只考虑扩容
        // 2. 动态空间，用于极限性能测试--扩容
        ensureEnoughSize(len);
        // 1. 将数据拷贝进缓冲区
        std::copy(data, data + len, &_buffer[_writer_idx]);
        // 2. 将当前写入位置向后偏移
        moveWriter(len);
    }

    // 获取缓冲区剩余可写空间大小，仅针对固定大小缓冲区，因为扩容思路下总是可写
    size_t writeAbleSize() {
        return (_buffer.size() - _writer_idx);
    }

    // 返回可读数据的起始地址
    const char* begin() {
        return &_buffer[_reader_idx];
    }

    // 返回可读数据的长度
    size_t readAbleSize() {
        // 因为当前实现的缓冲区设计思想是双缓冲区，处理完就交换，所以不存在空间循环使用的情况
        return (_writer_idx - _reader_idx);
    }

    // 移动读指针，偏移量为len，前提是len不超过可读数据长度
    void moveReader(size_t len) {
        assert(len <= readAbleSize());
        _reader_idx += len;
    }

    // 重置读写位置，初始化缓冲区，使所有空间空闲且无数据可读
    void reset() {
        _writer_idx = 0;
        _reader_idx = 0;
    }

    // 对Buffer实现交换操作，交换缓冲区内容、读索引和写索引
    void swap(Buffer &buffer) {
        _buffer.swap(buffer._buffer);
        std::swap(_reader_idx, buffer._reader_idx);
        std::swap(_writer_idx, buffer._writer_idx);
    }

    // 判断缓冲区是否为空，读写索引相等则表示无数据
    bool empty() {
        return (_reader_idx == _writer_idx);
    }

private:
    // 对空间进行扩容
    void ensureEnoughSize(size_t len) {
        if (len <= writeAbleSize()) return ;//不需要扩容
        size_t new_size = 0;
        if (_buffer.size() < BUFFER_THRESHOLD_SIZE) {
            new_size = _buffer.size() * 2 + len; //小于阈值则翻倍增长
        } else {
            new_size = _buffer.size() + BUFFER_INCREMENT_SIZE + len;//否则线性增长
        }
        _buffer.resize(new_size);
    }

    // 对写指针进行向后偏移操作，偏移量为len，前提是len不超过缓冲区大小
    void moveWriter(size_t len) {
        assert(len <= _buffer.size());
        _writer_idx += len;
    }

private:
    std::vector<char> _buffer; // 存储日志数据的缓冲区
    size_t _writer_idx; // 写索引，指向可写区域起始位置
    size_t _reader_idx; // 读索引，指向可读区域起始位置
};
}
#endif